package com.hxkj.token;

import com.alibaba.fastjson.JSONObject;
import com.hxkj.token.enums.UserRedisKeyEnum;
import com.hxkj.token.po.LoginToken;
import com.hxkj.token.po.Token;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.text.MessageFormat;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.TimeUnit;


/**
 * token生成工具
 */
@Service
@Configuration
@Slf4j
@Data
public class TokenGenerator {

    public static final String KEY_ALGORITHM = "DES";

    public static final String CIPHER_ALGORITHM_ECB = "DES/ECB/PKCS5Padding";

    private final static String stringKey = "5b8e6636-c782-4ff9-9e6f-de63b6ffc354";

    private final static String owmuser = "cms-management";

    // accessToken
    @Value("${auth.token.accessToken.expire}")
    private Integer accessTokenExpire;

    // refreshToken
    @Value("${auth.token.refreshToken.expire}")
    private Integer refreshTokenExpire;

    private SecretKey deskey; // 秘钥

    @Autowired
    private StringRedisTemplate redisTemplate;

    private String tokenRedisKey = "token:v:{0}:{1}:{2}";

    /**
     * 生成token
     *
     * @param userId
     * @return
     * @throws Exception
     */
    public String generatorToken(String mobile, String account, String userId, String type, Integer expires) throws Exception {
        if (deskey == null) {
            init();
        }
        Token token = new Token();
        if (!StringUtils.isEmpty(mobile)) {
            token.setMobile(mobile);
        }
        token.setUserId(userId);
        token.setAccount(account);
        token.setType(type);
        token.setCreateDate(new Date().getTime());
        token.setOwmuser(owmuser);
        token.setExpires(expires);
        // 存储到Redis中设置过期时间
        String stringToken = encrytor(JSONObject.toJSONString(token));
        // 生成token 加入生成时间
        redisTemplate.opsForValue().set(MessageFormat.format(tokenRedisKey, token.getUserId(), token.getType(), token.getCreateDate()), "1", token.getExpires(), TimeUnit.HOURS);
        return stringToken;
    }

    /**
     * 验证token是否有效
     *
     * @param tokenString
     * @return
     */
    public Token validateToken(String tokenString) {
        try {
            if (deskey == null) {
                init();
            }
            if (StringUtils.isBlank(tokenString)) {
                return null;
            }
            String str = decryptor(tokenString);
            Token token = JSONObject.parseObject(str, Token.class);
            if (token == null) {
                return null;
            }
            // redis 中的token 和验证出来的token进行对比,判断
            boolean existOk = redisTemplate.hasKey(MessageFormat.format(tokenRedisKey, token.getUserId(), token.getType(), token.getCreateDate()));
            if (!existOk) {
                return null;
            }
            // 重置token过期时间
            redisTemplate.expire(MessageFormat.format(tokenRedisKey, token.getUserId(), token.getType(), token.getCreateDate()), token.getExpires(), TimeUnit.HOURS);

            return token;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 初始化加密文字
     *
     * @throws Exception
     */
    public synchronized void init() throws Exception {
        byte[] key = stringKey.getBytes();
        // 创建一个空的8位字节数组（默认值为0）
        byte[] encodedKey = new byte[8];
        // 将原始字节数组转换为8位
        for (int i = 0; i < key.length && i < encodedKey.length; i++) {
            encodedKey[i] = key[i];
        }
        deskey = new SecretKeySpec(encodedKey, 0, encodedKey.length, KEY_ALGORITHM);
    }

    /**
     * 加密
     *
     * @param str 明文
     * @return 密文, base64编码
     */
    public String encrytor(String str) throws Exception {
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_ECB);
        cipher.init(Cipher.ENCRYPT_MODE, deskey);
        byte[] target = cipher.doFinal(str.getBytes());
        return Base64.encodeBase64URLSafeString(target);
    }

    /**
     * 解密
     *
     * @param str 密文,base64编码
     * @return 明文
     * @throws Exception
     */
    public String decryptor(String str) throws Exception {
        byte[] src = Base64.decodeBase64(str);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_ECB);
        cipher.init(Cipher.DECRYPT_MODE, deskey);
        return new String(cipher.doFinal(src));
    }


    /**
     * 设置自动过期时间
     *
     * @throws Exception
     */
    public void generatorOut() throws Exception {

        ServletRequestAttributes servletRequestAttributes = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());
        HttpServletRequest request = servletRequestAttributes.getRequest();
//        String s = MessageFormat.format(tokenRedisKey, userId);
//        log.debug(s);
//        Set<String> keys = redisTemplate.keys(MessageFormat.format("token:v:{0}:*", userId));
//        redisTemplate.delete(keys);
        String accesstoken = request.getHeader("accesstoken");
        Token token = this.validateToken(accesstoken);
        redisTemplate.expire(MessageFormat.format(tokenRedisKey, token.getUserId(), token.getType(), token.getCreateDate()), 0, TimeUnit.SECONDS);
    }

    public void generatorOutAll(String userId) throws Exception {
        ServletRequestAttributes servletRequestAttributes = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());
        HttpServletRequest request = servletRequestAttributes.getRequest();
        String s = MessageFormat.format(tokenRedisKey, userId);
        log.debug(s);
        Set<String> keys = redisTemplate.keys(MessageFormat.format("token:v:{0}:*", userId));
        redisTemplate.delete(keys);
        String accesstoken = request.getHeader("accesstoken");
        Token token = this.validateToken(accesstoken);
        redisTemplate.expire(MessageFormat.format(tokenRedisKey, token.getUserId(), token.getType(), token.getCreateDate()), 0, TimeUnit.SECONDS);
    }


    public LoginToken generatorLoginToken(String mobile, String account, String userId) throws Exception {
        String accessToken = generatorToken(mobile, account, userId, "", accessTokenExpire);
        String refreshToken = generatorToken(mobile, account, userId, "", refreshTokenExpire);

        // TODO 对accesstokne和freshtoken分别设置过期时间 并且增加刷新token接口
        LoginToken loginToken = new LoginToken();
        loginToken.setAccessToken(accessToken);
        loginToken.setRefreshToken(refreshToken);
        loginToken.setExpires(accessTokenExpire);
        return loginToken;
    }


    /**
     * 清除token用户信息
     *
     * @param userId
     * @throws Exception
     */
    public void invalidUserInfo(String userId) throws Exception {
        if (redisTemplate.hasKey(MessageFormat.format(UserRedisKeyEnum.USER_ALL_INFO.getKey(), userId))){
            redisTemplate.delete(MessageFormat.format(UserRedisKeyEnum.USER_ALL_INFO.getKey(), userId));
        }
    }

}
